# -*- coding: utf-8 -*-
'''
Selenium Webdriver remote test-runner for Qooxdoo test-runner (console view).
Basically it works just like ``qx-phantom.js``, see ``./source/script/headless``.
The point is to be able to run in multiple browsers, whereas ``qx-phantom.js`` is
limmited to PhantomJS. 

To run local headless PhantomJS test, type ``python -m unittest -v test.Suite``.

For convinience, scheduling test suite in saucalab.com is implemented as a test suite.
To run, type ``python -m unittest -v test.Schedule``.

To summarize employed test environments here's the list:

  * Qooxdoo ``test-source`` and ``test`` jobs,
  * Custom ``test-console`` job, based on ``test-source`` job with console view 
    for ``qx-phantom.js`` and GhostDriver (``test.Suite``),
  * Custom ``test-remote`` job, based on ``test`` job, with ``qx-saucelabs.js``
    auto run and compatible result converter (https://saucelabs.com/u/saaj). 

@author: saaj
'''


import unittest
import collections
import urllib2
import json
import os

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait


class Suite(unittest.TestSuite):
  
  _driver = None
  '''Webdriver instance'''
  
  
  def __init__(self):
    self._driver = self._createWebdriver()
    
    cases = self._createTestCases(self._loadQooxdooTests()) 
    unittest.TestSuite.__init__(self, cases)
  
  def __del__(self):
    self._driver.quit()
  
  def _createWebdriver(self):
    return webdriver.PhantomJS()
  
  def _loadQooxdooTests(self):
    self._driver.get('test-console/index-source.html?testclass=lastfmbackup.test')
    
    def testRunnerReady(driver):
      state = driver.execute_script('return qx.core.Init.getApplication().runner.getTestSuiteState()')
      if state in ('init', 'loading'):
        return False
      elif state == 'ready':
        return True
      elif state == 'error':
        log     = driver.get_log('browser')
        message = log[-1]['message'] if log else ''
        raise Exception('Test runner load error: {0}'.format(message))
      else:
        raise Exception('Unexpected test runner init state: "{0}"'.format(state))
      
    wait = WebDriverWait(self._driver, 4)
    wait.until(testRunnerReady)
    
    result = self._driver.execute_script('''
      var runner = qx.core.Init.getApplication().runner; 
      
      // store original test list
      runner.__testList = runner.testList;
       
      return runner.testList.map(function(t)
      {
        return t.fullName;
      });
    ''')
    
    return map(str, result)
      
  def _createTestCases(self, fullNames):
    cases = collections.defaultdict(list)
    for fullName in fullNames:
      cls, method = fullName.split(':')
      cases[cls].append(method)
    
    # ``unittest.loader.TestLoader.loadTestsFromTestCase`` adds to a suite
    # test case instance per its test method. It looks a little convoluted, 
    # from the first sight. 
    result = []
    for name, methodList in cases.items():
      caseClass = self._createTestCase(name, methodList)
      result   += map(caseClass, methodList)

    return result 
      
  def _createTestCase(self, name, methodList):
    return type(name, (unittest.TestCase,), {m: self._createTest(name, m) for m in methodList})
      
  def _createTest(self, className, methodName):
    
    driver = self._driver
    def test(self):
      fullName = '{0}:{1}'.format(className, methodName)
      
      driver.execute_script('''
        var runner    = qx.core.Init.getApplication().runner;
        var testToRun = runner.__testList.filter(function(t)
        {
          return t.fullName == '%s';
        });
        runner.setSelectedTests(new qx.data.Array(testToRun));
        runner.view.run();
      ''' % fullName)
      
      def testRunnerFinished(driver):
        state = driver.execute_script('return qx.core.Init.getApplication().runner.getTestSuiteState()')
        if state == 'running':
          return False
        elif state == 'finished':
          return True
        else:
          raise Exception('Unexpected test runner running state: "{0}"'.format(state))
        
      wait = WebDriverWait(driver, 30)
      wait.until(testRunnerFinished)
      
      result = driver.execute_script('return qx.core.Init.getApplication().runner.view.getTestResults()')
      if result[fullName]['state'] != 'success':
        self.fail(u'\n'.join(result[fullName]['messages']))
    
    return test
  
  
class Schedule(unittest.TestSuite):
  
  def __init__(self):
    unittest.TestSuite.__init__(self, ())
    
    self._scheduleSaucelabTests()
    
  def _scheduleSaucelabTests(self):
    username  = os.environ.get('SAUCE_USERNAME')
    accessKey = os.environ.get('SAUCE_ACCESS_KEY')
    
    url     = 'https://saucelabs.com/rest/v1/{0}/js-tests'.format(username) 
    headers = {'Content-Type': 'application/json'}
    body    = json.dumps({
      'name'      : 'Last.fm backup test suite',
      'tags'      : ['lastfmbackup'],
      'url'       : 'http://lastfm-backup.googlecode.com/hg/lastfmbackup/build/test/index.html?sauce',
      'framework' : 'custom',
      'platforms' : [
        ['Linux',     'firefox', ''],
        ['Linux',     'googlechrome', ''],
        ['Windows 7', 'firefox', ''],      
        ['Windows 7', 'googlechrome', ''],      
        ['OS X 10.6', 'firefox', ''],
        ['OS X 10.6', 'googlechrome', '']
      ],
      'record-video'       : False,
      'record-screenshots' : False    
    })
    
    manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
    manager.add_password(None, url, username, accessKey)
    urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(manager)))

    request  = urllib2.Request(url, body, headers)
    response = urllib2.urlopen(request)
    print response.read()

